/*
 *    Copyright 2009-2012 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package com.ibatis.dao.client.template;

import com.ibatis.common.util.PaginatedList;
import com.ibatis.dao.client.DaoException;
import com.ibatis.dao.client.DaoManager;
import com.ibatis.dao.engine.transaction.sqlmap.SqlMapDaoTransaction;
import com.ibatis.sqlmap.client.SqlMapExecutor;
import com.ibatis.sqlmap.client.SqlMapTransactionManager;
import com.ibatis.sqlmap.client.event.RowHandler;
import com.ibatis.sqlmap.engine.execution.BatchException;

import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/*
 * A DaoTemplate for SQL Map implementations that provides a
 * convenient method to access the SqlMapExecutor.  This class
 * also provides SqlMapExecutor method wrappers that conveniently
 * wrap SQLExceptions with DAO Exceptions.
 *
 * @author Zach Scott
 */
public abstract class SqlMapDaoTemplate extends DaoTemplate implements SqlMapExecutor {

  /*
   * The DaoManager that manages this Dao instance will be passed
   * in as the parameter to this constructor automatically upon
   * instantiation.
   *
   * @param daoManager
   */
  public SqlMapDaoTemplate(DaoManager daoManager) {
    super(daoManager);
  }

  /*
   * Gets the SQL Map Executor associated with the current
   * DaoTransaction that this Dao is working under.  The SqlMapExecutor
   * interface declares a number of methods for executing statements
   * via an SqlMapClient instance.
   *
   * @return A SqlMapExecutor instance.
   */
  protected SqlMapExecutor getSqlMapExecutor() {
    SqlMapDaoTransaction trans = (SqlMapDaoTransaction) daoManager.getTransaction(this);
    return trans.getSqlMap();
  }

  /*
   * Gets the SQL Map Transaction Manager associated with the current
   * DaoTransaction that this Dao is working under.  The SqlMapExecutor
   * interface declares a number of methods for executing statements
   * via an SqlMapClient instance.
   * <p/>
   * NOTE: It is rare to require this in a DAO.  Only very special
   * cases of DAO implementations will require access to the
   * SqlMapTransactionManager.  Messing with transactions at this
   * level might be dangerous to your data integrity (e.g. committing
   * too early).
   *
   * @return A SqlMapTransactionManager instance.
   */
  protected SqlMapTransactionManager getSqlMapTransactionManager() {
    SqlMapDaoTransaction trans = (SqlMapDaoTransaction) daoManager.getTransaction(this);
    return trans.getSqlMap();
  }

  /*
   * Executes a mapped SQL INSERT statement.
   * Insert is a bit different from other update methods, as it
   * provides facilities for returning the primary key of the
   * newly inserted row (rather than the effected rows).  This
   * functionality is of course optional.
   * <p/>
   * The parameter object is generally used to supply the input
   * data for the INSERT values.
   *
   * @param id              The name of the statement to execute.
   * @param parameterObject The parameter object (e.g. JavaBean, Map, XML etc.).
   * @return The primary key of the newly inserted row.  This might be automatically
   *         generated by the RDBMS, or selected from a sequence table or other source.
   */
  public Object insert(String id, Object parameterObject) {
    try {
      return getSqlMapExecutor().insert(id, parameterObject);
    } catch (SQLException e) {
      throw new DaoException("Failed to insert - id ["
          + id + "], parameterObject [" + parameterObject + "]. Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL INSERT statement.
   * Insert is a bit different from other update methods, as it
   * provides facilities for returning the primary key of the
   * newly inserted row (rather than the effected rows).  This
   * functionality is of course optional.
   * <p/>
   * This overload assumes no parameter is needed.
   *
   * @param id The name of the statement to execute.
   * @return The primary key of the newly inserted row.  This might be automatically
   *         generated by the RDBMS, or selected from a sequence table or other source.
   */
  public Object insert(String id) {
    try {
      return getSqlMapExecutor().insert(id);
    } catch (SQLException e) {
      throw new DaoException("Failed to insert - id ["
          + id + "]. Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL UPDATE statement.
   * Update can also be used for any other update statement type,
   * such as inserts and deletes.  Update returns the number of
   * rows effected.
   * <p/>
   * The parameter object is generally used to supply the input
   * data for the UPDATE values as well as the WHERE clause parameter(s).
   *
   * @param id              The name of the statement to execute.
   * @param parameterObject The parameter object (e.g. JavaBean, Map, XML etc.).
   * @return The number of rows effected.
   */
  public int update(String id, Object parameterObject) {
    try {
      return getSqlMapExecutor().update(id, parameterObject);
    } catch (SQLException e) {
      throw new DaoException("Failed to update - id ["
          + id + "] - parameterObject [" + parameterObject + "].  Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL UPDATE statement.
   * Update can also be used for any other update statement type,
   * such as inserts and deletes.  Update returns the number of
   * rows effected.
   * <p/>
   * This overload assumes no parameter is needed.
   *
   * @param id The name of the statement to execute.
   * @return The number of rows effected.
   */
  public int update(String id) {
    try {
      return getSqlMapExecutor().update(id);
    } catch (SQLException e) {
      throw new DaoException("Failed to update - id ["
          + id + "].  Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL DELETE statement.
   * Delete returns the number of rows effected.
   * <p/>
   * The parameter object is generally used to supply the input
   * data for the WHERE clause parameter(s) of the DELETE statement.
   *
   * @param id              The name of the statement to execute.
   * @param parameterObject The parameter object (e.g. JavaBean, Map, XML etc.).
   * @return The number of rows effected.
   */
  public int delete(String id, Object parameterObject) {
    try {
      return getSqlMapExecutor().delete(id, parameterObject);
    } catch (SQLException e) {
      throw new DaoException("Failed to delete - id ["
          + id + "] - parameterObject [" + parameterObject + "].  Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL DELETE statement.
   * Delete returns the number of rows effected.
   * <p/>
   * This overload assumes no parameter is needed.
   *
   * @param id The name of the statement to execute.
   * @return The number of rows effected.
   */
  public int delete(String id) {
    try {
      return getSqlMapExecutor().delete(id);
    } catch (SQLException e) {
      throw new DaoException("Failed to delete - id ["
          + id + "].  Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL SELECT statement that returns data to populate
   * a single object instance.
   * <p/>
   * The parameter object is generally used to supply the input
   * data for the WHERE clause parameter(s) of the SELECT statement.
   *
   * @param id              The name of the statement to execute.
   * @param parameterObject The parameter object (e.g. JavaBean, Map, XML etc.).
   * @return The single result object populated with the result set data.
   */
  public Object queryForObject(String id, Object parameterObject) {
    try {
      return getSqlMapExecutor().queryForObject(id, parameterObject);
    } catch (SQLException e) {
      throw new DaoException("Failed to execute queryForObject - id ["
          + id + "], parameterObject [" + parameterObject + "].  Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL SELECT statement that returns data to populate
   * a single object instance.
   * <p/>
   * This overload assumes no parameter is needed.
   *
   * @param id The name of the statement to execute.
   * @return The single result object populated with the result set data.
   */
  public Object queryForObject(String id) {
    try {
      return getSqlMapExecutor().queryForObject(id);
    } catch (SQLException e) {
      throw new DaoException("Failed to execute queryForObject - id ["
          + id + "].  Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL SELECT statement that returns data to populate
   * the supplied result object.
   * <p/>
   * The parameter object is generally used to supply the input
   * data for the WHERE clause parameter(s) of the SELECT statement.
   *
   * @param id              The name of the statement to execute.
   * @param parameterObject The parameter object (e.g. JavaBean, Map, XML etc.).
   * @param resultObject    The result object instance that should be populated with result data.
   * @return The single result object as supplied by the resultObject parameter, populated with the result set data.
   */
  public Object queryForObject(String id, Object parameterObject, Object resultObject) {
    try {
      return getSqlMapExecutor().queryForObject(id, parameterObject, resultObject);
    } catch (SQLException e) {
      throw new DaoException("Failed to queryForObject - id ["
          + id + "], parameterObject [" + parameterObject + "].  Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL SELECT statement that returns data to populate
   * a number of result objects.
   * <p/>
   * The parameter object is generally used to supply the input
   * data for the WHERE clause parameter(s) of the SELECT statement.
   *
   * @param id              The name of the statement to execute.
   * @param parameterObject The parameter object (e.g. JavaBean, Map, XML etc.).
   * @return A List of result objects.
   */
  public List queryForList(String id, Object parameterObject) {
    try {
      return getSqlMapExecutor().queryForList(id, parameterObject);
    } catch (SQLException e) {
      throw new DaoException("Failed to queryForList - id ["
          + id + "], parameterObject [" + parameterObject + "].  Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL SELECT statement that returns data to populate
   * a number of result objects.
   * <p/>
   * This overload assumes no parameter is needed.
   *
   * @param id The name of the statement to execute.
   * @return A List of result objects.
   */
  public List queryForList(String id) {
    try {
      return getSqlMapExecutor().queryForList(id);
    } catch (SQLException e) {
      throw new DaoException("Failed to queryForList - id ["
          + id + "].  Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL SELECT statement that returns data to populate
   * a number of result objects within a certain range.
   * <p/>
   * The parameter object is generally used to supply the input
   * data for the WHERE clause parameter(s) of the SELECT statement.
   *
   * @param id              The name of the statement to execute.
   * @param parameterObject The parameter object (e.g. JavaBean, Map, XML etc.).
   * @param skip            The number of results to ignore.
   * @param max             The maximum number of results to return.
   * @return A List of result objects.
   */
  public List queryForList(String id, Object parameterObject, int skip, int max) {
    try {
      return getSqlMapExecutor().queryForList(id, parameterObject, skip, max);
    } catch (SQLException e) {
      throw new DaoException("Failed to queryForList - id ["
          + id + "], parameterObject [" + parameterObject + "], skip ["
          + skip + "], max [" + max + "].  Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL SELECT statement that returns data to populate
   * a number of result objects within a certain range.
   * <p/>
   * This overload assumes no parameter is needed.
   *
   * @param id   The name of the statement to execute.
   * @param skip The number of results to ignore.
   * @param max  The maximum number of results to return.
   * @return A List of result objects.
   */
  public List queryForList(String id, int skip, int max) {
    try {
      return getSqlMapExecutor().queryForList(id, skip, max);
    } catch (SQLException e) {
      throw new DaoException("Failed to queryForList - id ["
          + id + "], skip ["
          + skip + "], max [" + max + "].  Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL SELECT statement that returns data to populate
   * a number of result objects that will be handled one at a time by a
   * RowHandler.
   * <p/>
   * This is generally a good approach to take when dealing with large sets
   * of records (i.e. hundreds, thousands...) that need to be processed without
   * eating up all of the system resources.
   * <p/>
   * The parameter object is generally used to supply the input
   * data for the WHERE clause parameter(s) of the SELECT statement.
   *
   * @param id              The name of the statement to execute.
   * @param parameterObject The parameter object (e.g. JavaBean, Map, XML etc.).
   * @param rowHandler      A RowHandler instance
   */
  public void queryWithRowHandler(String id, Object parameterObject, RowHandler rowHandler) {
    try {
      getSqlMapExecutor().queryWithRowHandler(id, parameterObject, rowHandler);
    } catch (SQLException e) {
      throw new DaoException("Failed to queryForList - id [" + id + "], parameterObject ["
          + parameterObject + "], rowHandler [ " + rowHandler + "].  Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL SELECT statement that returns data to populate
   * a number of result objects that will be handled one at a time by a
   * RowHandler.
   * <p/>
   * This is generally a good approach to take when dealing with large sets
   * of records (i.e. hundreds, thousands...) that need to be processed without
   * eating up all of the system resources.
   * <p/>
   * This overload assumes no parameter is needed.
   *
   * @param id         The name of the statement to execute.
   * @param rowHandler A RowHandler instance
   */
  public void queryWithRowHandler(String id, RowHandler rowHandler) {
    try {
      getSqlMapExecutor().queryWithRowHandler(id, rowHandler);
    } catch (SQLException e) {
      throw new DaoException("Failed to queryForList - id [" + id + "], rowHandler [ "
          + rowHandler + "].  Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL SELECT statement that returns data to populate
   * a number of result objects a page at a time.
   * <p/>
   * The parameter object is generally used to supply the input
   * data for the WHERE clause parameter(s) of the SELECT statement.
   *
   * @param id              The name of the statement to execute.
   * @param parameterObject The parameter object (e.g. JavaBean, Map, XML etc.).
   * @param pageSize        The maximum number of result objects each page can hold.
   * @return A PaginatedList of result objects.
   */
  public PaginatedList queryForPaginatedList(String id, Object parameterObject, int pageSize) {
    try {
      return getSqlMapExecutor().queryForPaginatedList(id, parameterObject, pageSize);
    } catch (SQLException e) {
      throw new DaoException("Failed to queryForPaginatedList - id [" + id + "], parameterObject ["
          + parameterObject + "], pageSize [" + pageSize + "].  Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL SELECT statement that returns data to populate
   * a number of result objects a page at a time.
   * <p/>
   * This overload assumes no parameter is needed.
   *
   * @param id       The name of the statement to execute.
   * @param pageSize The maximum number of result objects each page can hold.
   * @return A PaginatedList of result objects.
   */
  public PaginatedList queryForPaginatedList(String id, int pageSize) {
    return queryForPaginatedList(id, null, pageSize);
  }

  /*
   * Executes a mapped SQL SELECT statement that returns data to populate
   * a number of result objects that will be keyed into a Map.
   * <p/>
   * The parameter object is generally used to supply the input
   * data for the WHERE clause parameter(s) of the SELECT statement.
   *
   * @param id              The name of the statement to execute.
   * @param parameterObject The parameter object (e.g. JavaBean, Map, XML etc.).
   * @param keyProp         The property to be used as the key in the Map.
   * @return A Map keyed by keyProp with values being the result object instance.
   */
  public Map queryForMap(String id, Object parameterObject, String keyProp) {
    try {
      return getSqlMapExecutor().queryForMap(id, parameterObject, keyProp);
    } catch (SQLException e) {
      throw new DaoException("Failed to queryForMap - id [" + id + "], parameterObject ["
          + parameterObject + "], keyProp [" + keyProp + "].  Cause: " + e, e);
    }
  }

  /*
   * Executes a mapped SQL SELECT statement that returns data to populate
   * a number of result objects from which one property will be keyed into a Map.
   * <p/>
   * The parameter object is generally used to supply the input
   * data for the WHERE clause parameter(s) of the SELECT statement.
   *
   * @param id              The name of the statement to execute.
   * @param parameterObject The parameter object (e.g. JavaBean, Map, XML etc.).
   * @param keyProp         The property to be used as the key in the Map.
   * @param valueProp       The property to be used as the value in the Map.
   * @return A Map keyed by keyProp with values of valueProp.
   */
  public Map queryForMap(String id, Object parameterObject, String keyProp, String valueProp) {
    try {
      return getSqlMapExecutor().queryForMap(id, parameterObject, keyProp, valueProp);
    } catch (SQLException e) {
      throw new DaoException("Failed to queryForMap - id [" + id + "], parameterObject ["
          + parameterObject + "], keyProp [" + keyProp + "], valueProp ["
          + valueProp + "].  Cause: " + e, e);
    }
  }

  /*
   * Starts a batch in which update statements will be cached before being sent to
   * the database all at once. This can improve overall performance of updates update
   * when dealing with numerous updates (e.g. inserting 1:M related data).
   */
  public void startBatch() {
    try {
      getSqlMapExecutor().startBatch();
    } catch (SQLException e) {
      throw new DaoException("Failed to startBatch.  Cause: " + e, e);
    }
  }

  /*
   * Executes (flushes) all statements currently batched.
   */
  public int executeBatch() {
    try {
      return getSqlMapExecutor().executeBatch();
    } catch (SQLException e) {
      throw new DaoException("Failed to executeBatch.  Cause: " + e, e);
    }
  }

  /*
   * Executes (flushes) all statements currently batched.
   *
   * @return a List of BatchResult objects.  There will be one element in the
   *         list for each sub-batch executed.  A sub-batch is created by adding a statement
   *         to the batch that does not equal the prior statement.
   */
  public List executeBatchDetailed() {
    try {
      return getSqlMapExecutor().executeBatchDetailed();
    } catch (BatchException e) {
      throw new DaoException("Failed to executeBatch.  Cause: " + e, e);
    } catch (SQLException e) {
      throw new DaoException("Failed to executeBatch.  Cause: " + e, e);
    }
  }
}
